package com.myk.game.gobangchess.rooms;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.myk.game.gobangchess.*;
import com.myk.game.gobangchess.constants.ColorCost;
import com.myk.game.gobangchess.constants.Msger;
import com.myk.game.gobangchess.constants.PathCost;
import com.myk.game.gobangchess.signs.*;
import com.myk.game.gobangchess.utils.FileUtils;
import com.myk.game.gobangchess.utils.GridsUtils;
import com.myk.game.gobangchess.utils.Pair;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 自由便捷画板
 * <p>
 * 1、开发新的无规则画板，先设计，后开发。
 * 名曰“自由便捷画板”
 * 主要是棋子选择能力。
 * 选择棋子能够跳转到标准棋子库里选定，还能够组合自定义的字符和符号。
 * 棋盘也要能够有一定的自定义能力，但是初期会提供多种经典的预设，和格子染色预设。
 * 多种便捷模式切换，落子模式，删子模式，移子模式。
 * 每个人都能存储自己最多10个预设。 所有预设所有人都能看到
 * <p>
 * 划线都由落棋子实现，不支持画提示标记
 * <p>
 * 没有棋盘预设，棋盘预设都从存档里面取
 */
public class FreeQuickRoom extends Room {

    private GameStateContainer gameStateContainer; //当前游戏全局信息（默认只留它，其它几个管理器都根据它的数据来动态创建，以节省内存）
    private IRoomInterface currentInteractor; //当前交互者

    public FreeQuickRoom(String roomName) {
        super(roomName, "\t\t可以自由便捷的摆放棋子、设计棋盘、管理存档、模拟对弈，没有规则限制。", "自由便捷画板", GridsUtils.createEmptyGrids(1, 1), true, true);
        setSortingBottomValue(true); //自由便捷画板永远置底
        gameStateContainer = ArchiveDataManager.getRecentArchiveGameInfo(); //加载最新的存档，程序重启也能加载最新的
        currentInteractor = gameStateContainer;
    }

    @Override
    public String getRuleText() {
        return "可以自由便捷的摆放棋子、设计棋盘、管理存档、模拟对弈，没有规则限制。";
    }

    @Override
    protected void doStart() {
        DataManager.noticeAllGameStart(player_black, player_white, roomName);
        overFlag = 1;//重置结束状态：对弈进行中未结束
        playingStartTime = System.currentTimeMillis(); //重置对弈开始时间
        resetGrids();
        onStartPrepare(); //执行回调
    }

    @Override
    public int getCountdownSec() {
        return 300;
    }

    @Override
    public void setBlackPlayer(Player player) {
        if (player == player_white) {
            player_black = player;//支持个人模式的话 黑白都是player
        }
        else {
            removeSpectator(player); //从观众变成棋手
            player_black = player;
        }
        player.sendMessage(Msger.beChessPlayerSuccess, "Ok");
        noticeAllRefreshRoomInfo();
        DataManager.noticeAllRefreshRoomList();
        checkGameStart();
    }

    @Override
    public void setWhitePlayer(Player player) {
        if (player == player_black) {
            player_white = player; //支持个人模式的话 黑白都是player
        }
        else {
            removeSpectator(player);//从观众变成棋手
            player_white = player;
        }
        player.sendMessage(Msger.beChessPlayerSuccess, "Ok");
        noticeAllRefreshRoomInfo();
        DataManager.noticeAllRefreshRoomList();
        checkGameStart();
    }

    @Override
    public void playerLeaveRoom(Player player) {
        if (this.player_black == player && this.player_white == player) { //个人模式直接离开房间
            player_black = null;
            player_white = null;
            overFlag = 0;//重置结束状态：对弈进行中未结束
            cur = 0;
            resetGrids();//如果黑白都为空，那么对局清空
        }
        else { //非个人模式离开方
            if (this.player_black == player) {

                player_black = null;
                if (player_white == null) {
                    overFlag = 0;//重置结束状态：对弈进行中未结束
                    cur = 0;
                    resetGrids();//如果黑白都为空，那么对局清空
                }
                else {
                    if (overFlag == 1) { //对弈中离开时
                        gameTip = "黑方离开，等待黑方上座继续。";
                        player_white.clearCustomBottom();
                        player_white.dispatchCustomBottom();
                    }
                }
            }
            else if (this.player_white == player) {
                player_white = null;
                if (player_black == null) {
                    overFlag = 0;//重置结束状态：对弈进行中未结束
                    cur = 0;
                    resetGrids();//如果黑白都为空，那么对局清空
                }
                else {
                    if (overFlag == 1) { //对弈中离开时
                        gameTip = "白方离开，等待白方上座继续。";
                        player_black.clearCustomBottom();
                        player_black.dispatchCustomBottom();
                    }
                }
            }
            else {
                removeSpectator(player);
            }
        }

        removeSpectator(player);
        player.setEnteringRoom(null);
        if (player == player_black || player == player_white) {
            noticeAllRefreshGameInfo();   //观众离开房间不会影响局面不用刷新，在座玩家离开才会影响
        }
        noticeAllRefreshRoomInfo();
        DataManager.noticeAllRefreshRoomList();
        player.sendMessage(Msger.youNotMove, "Ok");

        System.out.println(player.getNickNameOrTemp() + "离开" + getRoomName());
    }

    @Override
    public void playerLeaveSeat(Player player) {
        //让座
        if (this.player_black == player && this.player_white == player) { //个人模式的情况
            player_black = null;
            player_white = null;
            overFlag = 0;//重置结束状态：对弈进行中未结束
            cur = 0;
            resetGrids();//如果黑白都为空，那么对局清空
        }
        else { //双人对战时的情况
            if (this.player_black == player) {
                player_black = null;
                if (player_white == null) { //如果玩家都走光，那么本对局清空
                    overFlag = 0;//重置结束状态：对弈进行中未结束
                    cur = 0;
                    resetGrids();//如果黑白都为空，那么对局清空
                }
                else {//座位只有一个人的时候等待黑方上座继续
                    gameTip = "黑方离开，等待黑方上座继续。";
                    player_white.clearCustomBottom();
                    player_white.dispatchCustomBottom();
                    resetGrids();//恢复显示游戏界面
                }
            }
            else if (this.player_white == player) {
                player_white = null;
                if (player_black == null) { //如果黑白都为空，那么对局清空
                    overFlag = 0;//重置结束状态：对弈进行中未结束
                    cur = 0;
                    resetGrids();//如果黑白都为空，那么对局清空
                }
                else { //座位只有一个人的时候等待白方上座继续
                    gameTip = "白方离开，等待白方上座继续。";
                    player_black.clearCustomBottom();
                    player_black.dispatchCustomBottom();
                    resetGrids();//恢复显示游戏界面
                }
            }
        }
        //成为观众
        removeSpectator(player); //先移出，避免因奇怪原因玩家多次进入，出现多个观众的问题
        addSpectator(player);
        //停止双方倒计时
        noticeAllCountdown(-1, -1);
        //取消双方触发棋盘事件的能力
        noticeNoMove();
        //清空按钮
        player.clearCustomBottom();
        player.dispatchCustomBottom();
        //通知刷新
        noticeAllRefreshGameInfo();
        noticeAllRefreshRoomInfo();
        DataManager.noticeAllRefreshRoomList();
    }

    @Override
    public void admitDefeat(Player player) {
        //++点认输的话，或许可以清空棋盘什么的
    }

    @Override
    public Player getCurPlayer() {
        if (gameStateContainer.side == 1) return player_black;
        if (gameStateContainer.side == 2) return player_white;
        else return null;
    }

    @Override
    public void resetGrids() {
        currentInteractor = gameStateContainer;
    }

    @Override
    public String getCoordMode() {
        return currentInteractor.getCoordMode();
    }

    @Override
    public String getGameInfo() {
        currentInteractor.render(this);
        return super.getGameInfo();
    }

    @Override
    public void doRule(int row, int col, int value) {
        currentInteractor.click(this, value, row, col);
    }

    @Override
    public void noticeCurMove() {
        this.cur = gameStateContainer.side; //玩家离开会让cur变为0，有新玩家上来时记得从这里更新cur
        super.noticeCurMove();
    }

    @Override
    public void noticeBlackMove() {
        super.noticeBlackMove();
        gameStateContainer.side = 1;
        if (player_black != null){
            currentInteractor.refreshButtons(this, player_black);
        }
    }

    @Override
    public void noticeWhiteMove() {
        super.noticeWhiteMove();
        gameStateContainer.side = 2;
        if (player_white != null){
            currentInteractor.refreshButtons(this, player_white);
        }
    }

    /**
     * 房间交互接口
     */
    private interface IRoomInterface {
        void click(FreeQuickRoom room, int side, int row, int col);

        void render(FreeQuickRoom freeQuickRoom);

        String getCoordMode();

        void refreshButtons(FreeQuickRoom room, Player player);
    }

    /**
     * 存档管理器
     */
    private static class ArchiveDataManager implements IRoomInterface {
        // 缓存数据文件夹路径
        private static final String roomDataPath = PathCost.ROOM_DATA_DIR + NoRuleRoom.class.getSimpleName() + "/";
        // 自动缓存文件夹路径，和用户自己的存档文件夹路径并列 //++用户离开作为，加载新存档时，都要先将当前的GameInfo添加到自动缓存文件夹中，自动缓存的上线也是10个，会以文件名称作记录判断，避免重复缓存。
        private static final String autoCacheDir = roomDataPath + "autoCache/";

        @Override
        public void click(FreeQuickRoom room, int side, int row, int col) {
            //++加载存档按钮
        }

        @Override
        public void render(FreeQuickRoom room) {
            room.curGrids = room.originGrids;
            room.signList.clear();
        }

        @Override
        public String getCoordMode() {
            return null;
        }

        @Override
        public void refreshButtons(FreeQuickRoom room, Player player) {
            //落子配置-取消按钮
            CustomBottom cb1 = new CustomBottom(room, "取消", (player2) -> GameStateContainer.showGaming(room));
            //存档管理-保存存档按钮
            CustomBottom cb2 = new CustomBottom(room, "保存存档", (player2) -> {
                //每人最多10个存档，超过10个得先删除旧
                // 有按钮去筛选自己的和他人的存档
                // 加载旧存档时，当前没有保存的GameInfo会缓存在运行时中，有个返回按钮切换回去
                //String jsonData = new Gson().toJson(room.gameStateContainer);
                //FileUtils.writeFileFromString(autoCacheDir + System.currentTimeMillis() + ".json", jsonData);
            });
            player.clearCustomBottom();
            player.registerCustomBottom(cb1, cb2);
        }

        /**
         * 获取最近的自动存档数据，如果没有或者数据文件损坏，就创建一个新的默认的
         */
        public static GameStateContainer getRecentArchiveGameInfo() {
            try {
                List<File> autoCacheFileList = FileUtils.listFilesInDir(autoCacheDir);
                if (autoCacheFileList.size() > 0) {
                    autoCacheFileList.sort((o1, o2) -> (int) (o2.lastModified() - o1.lastModified())); //根据创建时间降序
                    File file = autoCacheFileList.get(0);
                    String jsonData = FileUtils.readFile2String(file);
                    GameStateContainer gameStateContainer = new Gson().fromJson(jsonData, GameStateContainer.class); //反射加载出游戏状态对象
                    gameStateContainer.autoCacheNo = FileUtils.getFileNameNoExtension(file); //注入编号，放置重复覆盖缓存文件
                    return gameStateContainer;
                }
            }
            catch (JsonSyntaxException e) {
                throw new RuntimeException(e);
            }
            return GameStateContainer.createNormal(); //前面失败那就创建一个全新默认的
        }
    }

    /**
     * 棋盘设置管理器
     */
    private static class BoardSettingManager implements IRoomInterface {

        private static final int maxRow = 8; //管理器界面最大行数
        private static final int maxCol = 8; //管理器界面最大列数
        private static final int boardSizeTitleRow = 0; //棋盘大小标题行
        private static final int presetStyleTitleRow = 3; //预设样式标题行

        private static final int[] reduceRowPos = new int[]{2, 1}; //减行按钮位置
        private static final int[] addRowPos = new int[]{2, 2}; //加行按钮位置
        private static final int[] reduceColPos = new int[]{2, 5}; //减列按钮位置
        private static final int[] addColPos = new int[]{2, 6}; //加列按钮位置

        private static final String style_square = "方格";
        private static final String style_crossLine = "交叉线";
        private static final String style_circle = "圆格";
        private static final String style_empty = "空白";
        private static final String style_blackWhite = "正黑白格";
        private static final String style_blackWhite2 = "反黑白格";
        private static final String style_chineseChess = "中国象棋";
        private static final String style_shogi = "将棋";
        private static final String style_eternal = "小永恒棋";
        private static final String style_zodiac = "生肖棋弈";
        private static final String[] presetStyleNames = new String[]{style_square, style_crossLine, style_circle, style_empty, style_blackWhite, style_blackWhite2, style_chineseChess, style_shogi, style_eternal, style_zodiac}; //预设的棋盘样式名称
        private Board settingBoard; //当前设置的棋盘
        private boolean isEditPage; //当前是否在编辑页面 or 预览页面

        private void init(Board board) {
            settingBoard = Board.clone(board);
            isEditPage = true;
        }

        @Override
        public void click(FreeQuickRoom room, int side, int row, int col) {
            if (isEditPage) {
                //点击了减少行按钮
                if (row == reduceRowPos[0] && col == reduceRowPos[1]) {
                    if (settingBoard.row > 1) {
                        settingBoard.row -= 1;
                    }
                }
                //点击了增加行按钮
                else if (row == addRowPos[0] && col == addRowPos[1]) {
                    if (settingBoard.row < Board.MAX_RC) {
                        settingBoard.row += 1;
                    }
                }
                //点击了减少列按钮
                else if (row == reduceColPos[0] && col == reduceColPos[1]) {
                    if (settingBoard.col > 1) {
                        settingBoard.col -= 1;
                    }
                }
                //点击了增加列按钮
                else if (row == addColPos[0] && col == addColPos[1]) {
                    if (settingBoard.col < Board.MAX_RC) {
                        settingBoard.col += 1;
                    }
                }
                //点击了预设棋盘样式
                else if (row > presetStyleTitleRow){
                    int index = (row - presetStyleTitleRow - 1) * 4 + col / 2;
                    if (index < presetStyleNames.length) {

                        //++暂时只实现了方格，交叉线和空白，等都实现了，就把这段代码删了
                        if ( !(index == 0 ||  index == 1 || index == 3) ) {
                            return; //暂时只实现了方格，交叉线和空白
                        }

                        settingBoard.bgStyle = presetStyleNames[index];
                        if (presetStyleNames[index].equals(style_empty)){
                            settingBoard.coordMode = "00000000";
                        }
                        else {
                            settingBoard.coordMode = "33000000";
                        }
                    }
                }
                room.noticeAllRefreshGameInfo();
            }
        }

        @Override
        public void render(FreeQuickRoom room) {
            //编辑模式下ui
            if (isEditPage) {
                room.curGrids = GridsUtils.createEmptyGrids(maxRow, maxCol);
                room.signList.clear();
                //绘制棋盘大小标题
                room.signList.add(new GroundSign(room.getIndex(boardSizeTitleRow, 0), room.getIndex(boardSizeTitleRow, maxCol - 1), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new TitleSign(0.5f, 0.5f / maxRow, 0, "棋盘大小：", ColorCost.BLACK, 0.5f));
                //绘制预设样式标题
                room.signList.add(new GroundSign(room.getIndex(presetStyleTitleRow, 0), room.getIndex(presetStyleTitleRow, maxCol - 1), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new TitleSign(0.5f, (presetStyleTitleRow + 0.5f) / maxRow, 0, "预设样式：", ColorCost.BLACK, 0.5f));

                //绘制棋盘大小设置UI
                //行
                room.signList.add(new TextSign(room.getIndex(1, 0), "行", ColorCost.BLACK));
                //room.signList.add(new GroundSign(room.getIndex(1, 0), room.getIndex(2, 0), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new GroundSign(room.getIndex(1, 1), room.getIndex(1, 2), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new GroundSign(room.getIndex(1, 3), room.getIndex(2, 3), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new TitleSign(2f / maxCol, 1.45f / maxRow, 0, settingBoard.row + "", ColorCost.BLACK, 0.75f)); //行数
                room.signList.add(new TextSign(room.getIndex(reduceRowPos[0], reduceRowPos[1]), "-", ColorCost.BLACK));
                room.signList.add(new TextSign(room.getIndex(addRowPos[0], addRowPos[1]), "+", ColorCost.BLACK));
                //列
                room.signList.add(new TextSign(room.getIndex(1, 4), "列", ColorCost.BLACK));
                //room.signList.add(new GroundSign(room.getIndex(1, 4), room.getIndex(2, 4), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new GroundSign(room.getIndex(1, 5), room.getIndex(1, 6), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new GroundSign(room.getIndex(1, 7), room.getIndex(2, 7), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                room.signList.add(new TitleSign(6f / maxCol, 1.45f / maxRow, 0, settingBoard.col + "", ColorCost.BLACK, 0.75f)); //列数
                room.signList.add(new TextSign(room.getIndex(reduceColPos[0], reduceColPos[1]), "-", ColorCost.BLACK));
                room.signList.add(new TextSign(room.getIndex(addColPos[0], addColPos[1]), "+", ColorCost.BLACK));

                //绘制预设的棋盘样式
                int styleIndex = 0;
                for (int i = presetStyleTitleRow + 1; i < maxRow; i++) {
                    for (int j = 0; j < maxCol; j += 2) {
                        room.signList.add(new GroundSign(room.getIndex(i, j), room.getIndex(i, j + 1), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                        if (styleIndex < presetStyleNames.length) {
                            String styleName = presetStyleNames[styleIndex];
                            int len = styleIndex * 2 / maxCol;
                            room.signList.add(new TitleSign((j + 1f) / maxCol, (presetStyleTitleRow + len + 1.45f) / maxRow, 0, styleName, styleName.equals(settingBoard.bgStyle) ? ColorCost.NEW_PUT_POINT_LIGHT : ColorCost.BLACK, 0.45f));
                        }
                        styleIndex++;
                    }
                }
            }
            //预览模式下
            else {
                settingBoard.render(room);
                //添加标题提示当前是预览界面
                room.signList.add(new TitleSign(0.5f, 0.5f, 0, "预览模式", "#44DD3333", settingBoard.col / 8f)); //列数
            }
        }

        @Override
        public String getCoordMode() {
            if (isEditPage) {
                return "00000000";
            }
            else {
                return settingBoard.getCoordMode();
            }
        }

        @Override
        public void refreshButtons(FreeQuickRoom room, Player player) {
            //棋盘设置-取消按钮
            CustomBottom cb1 = new CustomBottom(room, "取消", (player2) -> GameStateContainer.showGaming(room));
            //棋盘设置-取消按钮
            CustomBottom cb2 = new CustomBottom(room, "重置", (player2) -> {
                settingBoard = new Board();
                room.noticeAllRefreshGameInfo();
            });
            //棋盘设置-编辑or预览按钮
            CustomBottom cb3;
            if (isEditPage) {
                cb3 = new CustomBottom(room, "预览", (player2) -> {
                    isEditPage = !isEditPage;
                    refreshButtons(room, player);
                    room.noticeAllRefreshGameInfo();
                });
            }
            else {
                cb3 = new CustomBottom(room, "编辑", (player2) -> {
                    isEditPage = !isEditPage;
                    refreshButtons(room, player);
                    room.noticeAllRefreshGameInfo();
                });
            }
            //棋盘设置-清空按钮
            CustomBottom cb4 = new CustomBottom(room, "新建", (player2) -> {
                room.gameStateContainer.boardChessList.clear();
                room.gameStateContainer.board = Board.clone(settingBoard);
                GameStateContainer.showGaming(room);
            });
            player.clearCustomBottom();
            player.registerCustomBottom(cb1, cb2, cb3, cb4);
        }

        /**
         * 创建一个棋盘设置管理器，根据当前游戏状态初始化
         */
        public static IRoomInterface createNow(FreeQuickRoom room) {
            BoardSettingManager manager = new BoardSettingManager();
            manager.init(room.gameStateContainer.board);
            return manager;
        }

    }

    /**
     * 落子预设管理器
     */
    private static class ChessPresetManager implements IRoomInterface {
        public static final int PRESET_CHESSLIST_MAX = 12;//最多同时预设12个
        public static final String DEFAULT_TEXT_COLOR_BLACK = "#FF000000"; //默认棋子的文字颜色-黑
        public static final String DEFAULT_TEXT_COLOR_GRAY = "#FF888888"; //默认棋子的文字颜色-黑
        public static final String DEFAULT_TEXT_COLOR_WHITE = "#FFFFFFFF"; //默认棋子的文字颜色-白
        private static final int maxRow = 12; //管理器界面最大行数
        private static final int maxCol = 12; //管理器界面最大列数
        private static final int topTitleRow = 0;//顶部标题行
        private static final int textPageTitleRow = 6; //文字子分页标题行
        private static final String textPage_number = "数字"; //阿拉伯数字加其它类型数字（不包括中文）
        private static final String textPage_letter = "字母"; //大小写字母
        private static final String textPage_symbol = "符号"; //方向符号，其它符号
        private static final String textPage_chinese = "汉字"; //汉字
        private static final String textPage_other = "其它"; //元素类型中文
        private static final String textPage_custom = "输入"; //用ask弹窗输入
        private static final String[] textPageTitles = {textPage_number, textPage_letter, textPage_symbol, textPage_chinese, textPage_other, textPage_custom}; //文字子分页标题
        private static final int pageTitleRow = 7; //分页标题行
        private static final String pageTitle_value = "棋子底座";
        private static final String pageTitle_text = "文字设置";
        private static final String pageTitle_color = "颜色设置";
        private static final String pageTitle_often = "常用预设";
        private static final String[] pageTitles = {pageTitle_value, pageTitle_text, pageTitle_color, pageTitle_often}; //棋子分页标题
        private static final int fallModeTitleRow = 8; //落子模式标题行
        private static final int fallModeRow = 9; //落子模式选项行
        public static final String fallMode_switch = "连点切换";
        public static final String fallMode_choose = "选子移动";
        public static final String fallMode_stack = "选取堆叠";
        public static final String fallMode_random = "随机落子";
        public static final String fallMode_line = "线性铺设";
        public static final String fallMode_rectangle = "矩形铺设";
        public static final String[] fallModeTitles = {fallMode_switch, fallMode_choose, fallMode_stack, fallMode_random, fallMode_line, fallMode_rectangle};
        private static final int previewChessListTitleRow = 10; //预览棋子序列的标题在倒数第二行
        private static final int previewChessListRow = 11; //预览棋子在最后一行


        //子分页索引
        private int pageIndex; //0：棋子底座，1：文字设置，2：颜色设置，3：常用预设
        //文字子分页索引
        private int textPageIndex;
        //可选配置内容
        private List<Chess> optionalConfigSet;
        //修改的落子模式
        private int previewFallModeIndex;
        //修改配置的棋子序列预览
        private List<Chess> previewChessList;
        //修改棋子索引
        private int presetIndex;
        //想要作用到的目标
        private Hand targetHand;

        //初始化
        public void init(Hand hand) {
            //根据当前操纵方的手，初始化预设棋子预览序列
            this.targetHand = hand;
            for (int i = 0; i < fallModeTitles.length; i++) {
                if (fallModeTitles[i].equals(targetHand.fallMode)) {
                    this.previewFallModeIndex = i;
                    break;
                }
            }
            this.previewChessList = new ArrayList<>(targetHand.chessSequence);
            this.presetIndex = this.previewChessList.size() - 1;
            switchValuePage(); //默认显示常用预设
        }

        //切换棋子底座分页
        private void switchValuePage() {
            this.pageIndex = 0;
            List<Chess> set = new ArrayList<>();
            //第一行
            set.add(new Chess(1)); //圆形黑棋
            set.add(new Chess(2)); //圆形白棋
            set.add(new Chess(7)); //圆形灰棋
            set.add(new Chess(8)); //圆形混棋
            set.add(new Chess(3)); //圆形红棋
            set.add(new Chess(4)); //圆形蓝棋
            set.add(new Chess(5)); //圆形黄棋
            set.add(new Chess(6)); //圆形绿棋
            set.add(new Chess(21)); //通常向上木片
            set.add(new Chess(22)); //通常向下木片
            set.add(new Chess(23)); //通常向左木片
            set.add(new Chess(24)); //通常向右木片
            //第二行
            set.add(new Chess(11)); //方形黑棋
            set.add(new Chess(12)); //方形白棋
            set.add(new Chess(17)); //方形灰棋
            set.add(new Chess(9)); //红墙
            set.add(new Chess(13)); //方形红棋
            set.add(new Chess(14)); //方形蓝棋
            set.add(new Chess(15)); //方形黄棋
            set.add(new Chess(16)); //方形绿棋
            set.add(new Chess(31)); //聚焦向上木片
            set.add(new Chess(32)); //聚焦向下木片
            set.add(new Chess(33)); //聚焦向左木片
            set.add(new Chess(34)); //聚焦向右木片
            this.optionalConfigSet = new ArrayList<>(set);
        }

        //切换文字设置分页
        private void switchTextPage() {
            this.pageIndex = 1;

            //数字子分页
            if (textPageTitles[textPageIndex].equals(textPage_number)) {
                switchTextPageNumber();
            }
            //字母子分页
            else if (textPageTitles[textPageIndex].equals(textPage_letter)) {
                switchTextPageLetter();
            }
            //符号子分页
            else if (textPageTitles[textPageIndex].equals(textPage_symbol)) {
                //switchTextPageSymbol();
            }
            //汉字子分页
            else if (textPageTitles[textPageIndex].equals(textPage_chinese)) {
                switchTextPageChinese();
            }
            //其它子分页
            else if (textPageTitles[textPageIndex].equals(textPage_other)) {
                //switchTextPageElement();
            }
            //自定义
            else if (textPageTitles[textPageIndex].equals(textPage_custom)) {
                //++注册一个ask弹窗接收用户的输入
            }


            //棋子文字：
            // 字符：（可多个）
            // 字母：ABCDEFGHIJKLMNOPQRSTUVWXYZ，abcdefghijklmnopqrstuvwxyz
            // 数字：1234567890
            // 方向：←，→，↑，↓，↔，↕，↖，↗，↘，↙
            // 汉字：（仅单个）
            //象棋（18），车马炮象士卒将后，車馬砲相仕兵帅後，帝皇王公主，弓骑步，刀剑斧锤，枪刺卫盾，旗营
            //元素（11），金木水火土，冰炎光暗，风云雷电，春夏秋冬，雾雨雪霜，日月星，天地人，生死时空，正反阴阳
            //生肖（12），鼠牛虎兔龙蛇马羊猴鸡狗猪
            //*再看看能不能用ask弹窗限制输入，让用户自己输入。
            //文字颜色：
            //# F、F、F、F、F、F、F、F、
            //0，1，2，3，4，5，6，7，8，9，
            //A，B、C、D、E、F，√，X
            //边框颜色：
            //# F、F、F、F、F、F、F、F、
            //0，1，2，3，4，5，6，7，8，9，
            //A，B、C、D、E、F，√，X
            //背景颜色：
            //# F、F、F、F、F、F、F、F、
            //0，1，2，3，4，5，6，7，8，9，
            //A，B、C、D、E、F，√，X
            //预览列表：（有限长度，可选编辑）
            //空、空、空、空、空、空、空、空、空、空、空
        }

        //切换文字子分页-数字
        private void switchTextPageNumber() {
            this.textPageIndex = 0;
            List<Chess> set = new ArrayList<>();
            //第一行
            set.add(new Chess().withText("0"));
            set.add(new Chess().withText("1"));
            set.add(new Chess().withText("2"));
            set.add(new Chess().withText("3"));
            set.add(new Chess().withText("4"));
            set.add(new Chess().withText("5"));
            set.add(new Chess().withText("6"));
            set.add(new Chess().withText("7"));
            set.add(new Chess().withText("8"));
            set.add(new Chess().withText("9"));
            set.add(new Chess());
            set.add(new Chess());
            //第二行
            set.add(new Chess().withText("①"));
            set.add(new Chess().withText("②"));
            set.add(new Chess().withText("③"));
            set.add(new Chess().withText("④"));
            set.add(new Chess().withText("⑤"));
            set.add(new Chess().withText("⑥"));
            set.add(new Chess().withText("⑦"));
            set.add(new Chess().withText("⑧"));
            set.add(new Chess().withText("⑨"));
            set.add(new Chess().withText("⑩"));
            set.add(new Chess());
            set.add(new Chess());
            //第三行
            set.add(new Chess().withText("Ⅰ"));
            set.add(new Chess().withText("Ⅱ"));
            set.add(new Chess().withText("Ⅲ"));
            set.add(new Chess().withText("Ⅳ"));
            set.add(new Chess().withText("Ⅴ"));
            set.add(new Chess().withText("Ⅵ"));
            set.add(new Chess().withText("Ⅶ"));
            set.add(new Chess().withText("Ⅷ"));
            set.add(new Chess().withText("Ⅸ"));
            set.add(new Chess().withText("Ⅹ"));
            set.add(new Chess().withText("Ⅺ"));
            set.add(new Chess().withText("Ⅻ"));
            this.optionalConfigSet = new ArrayList<>(set);
        }

        //切换文字子分页-字母
        private void switchTextPageLetter() {
            this.textPageIndex = 1;
            List<Chess> set = new ArrayList<>();
            //26个小写字母
            for (int i = 0; i < 26; i++) {
                set.add(new Chess().withText(String.valueOf((char) (97 + i))));
            }
            //8个空格
            for (int i = 0; i < 8; i++) {
                set.add(new Chess());
            }
            //26个大写字母
            for (int i = 0; i < 26; i++) {
                set.add(new Chess().withText(String.valueOf((char) (65 + i))));
            }
            this.optionalConfigSet = new ArrayList<>(set);
        }

        //切换文字子分页-符号
        private void switchTextPageSymbol() {
            this.textPageIndex = 2;
            List<Chess> set = new ArrayList<>();

            set.add(new Chess().withText("←"));
            set.add(new Chess().withText("→"));
            set.add(new Chess().withText("↑"));
            set.add(new Chess().withText("↓"));
            set.add(new Chess().withText("↔"));
            set.add(new Chess().withText("↕"));
            set.add(new Chess().withText("↖"));
            set.add(new Chess().withText("↗"));
            set.add(new Chess().withText("↘"));
            set.add(new Chess().withText("↙"));
            set.add(new Chess());
            set.add(new Chess());
            set.add(new Chess().withText("√"));
            set.add(new Chess().withText("X"));
            set.add(new Chess().withText("+"));
            set.add(new Chess().withText("-"));
            set.add(new Chess().withText("*"));
            set.add(new Chess().withText("÷"));
            set.add(new Chess().withText("="));
            set.add(new Chess().withText("%"));
            set.add(new Chess().withText("@"));
            set.add(new Chess().withText("."));
            set.add(new Chess());
            set.add(new Chess());

            //第四行
            set.add(new Chess().withText("⚀"));
            set.add(new Chess().withText("⚁"));
            set.add(new Chess().withText("⚂"));
            set.add(new Chess().withText("⚃"));
            set.add(new Chess().withText("⚄"));
            set.add(new Chess().withText("⚅"));

            this.optionalConfigSet = new ArrayList<>(set);
        }

        //切换文字子分页-汉字
        private void switchTextPageChinese() {
            //象棋（18），车马炮象士卒将后，車馬砲相仕兵帅後，帝皇王公主，弓骑步，刀剑斧锤，枪刺卫盾，旗营
            //元素（11），金木水火土，冰炎光暗，雷电风云，春夏秋冬，雾雨雪霜，日月星，天地人，生死时空，正反阴阳
            //生肖（12），鼠牛虎兔龙蛇马羊猴鸡狗猪
            this.textPageIndex = 3;
            List<Chess> set = new ArrayList<>();
            //第一行：生肖
            set.add(new Chess().withText("鼠"));
            set.add(new Chess().withText("牛"));
            set.add(new Chess().withText("虎"));
            set.add(new Chess().withText("兔"));
            set.add(new Chess().withText("龙"));
            set.add(new Chess().withText("蛇"));
            set.add(new Chess().withText("马"));
            set.add(new Chess().withText("羊"));
            set.add(new Chess().withText("猴"));
            set.add(new Chess().withText("鸡"));
            set.add(new Chess().withText("狗"));
            set.add(new Chess().withText("猪"));
            //第二行：元素
            set.add(new Chess().withText("金"));
            set.add(new Chess().withText("木"));
            set.add(new Chess().withText("水"));
            set.add(new Chess().withText("土"));
            set.add(new Chess().withText("火"));
            set.add(new Chess().withText("冰"));
            set.add(new Chess().withText("雷"));
            set.add(new Chess().withText("风"));
            set.add(new Chess().withText("光"));
            set.add(new Chess().withText("暗"));
            set.add(new Chess().withText("天"));
            set.add(new Chess().withText("地"));

            this.optionalConfigSet = new ArrayList<>(set);
        }

        //切换颜色设置分页
        private void switchColorPage() {
            this.pageIndex = 2;
            List<Chess> set = new ArrayList<>();
            //第一行
            set.add(new Chess().withTextAndColor("黑","#FF000000")); //黑色
            set.add(new Chess().withTextAndColor("白","#FFFFFFFF")); //白色
            set.add(new Chess().withTextAndColor("灰","#FF808080")); //灰色
            set.add(new Chess().withTextAndColor("银","#FFC0C0C0")); //银色
            set.add(new Chess().withTextAndColor("蓝","#FF0000FF")); //蓝色
            set.add(new Chess().withTextAndColor("绿","#FF00FF00")); //绿色
            set.add(new Chess().withTextAndColor("红","#FFFF0000")); //红色
            set.add(new Chess().withTextAndColor("黄","#FFFFFF00")); //黄色
            set.add(new Chess().withTextAndColor("青","#FF00FFFF")); //青色
            set.add(new Chess().withTextAndColor("紫","#FFFF00FF")); //紫色
            set.add(new Chess().withTextAndColor("橙","#FFFFA500")); //橙色
            this.optionalConfigSet = new ArrayList<>(set);
        }

        //切换常用预设分页
        private void switchOftenPage() {
            this.pageIndex = 3;
            List<Chess> set = new ArrayList<>();
            //第一行
            set.add(new Chess(1)); //圆形黑棋
            set.add(new Chess(2)); //圆形白棋
            set.add(new Chess(7)); //圆形灰棋
            set.add(new Chess(11)); //方形黑棋
            set.add(new Chess(12)); //方形白棋
            set.add(new Chess(17)); //方形灰棋
            set.add(new Chess(8)); //红墙
            set.add(new Chess(3)); //圆形红棋
            set.add(new Chess(4)); //圆形蓝棋
            set.add(new Chess(5)); //圆形黄棋
            set.add(new Chess(6)); //圆形绿棋
            set.add(new Chess()); //空占位
            //第二行
            set.add(new Chess(1).withText("車"));
            set.add(new Chess(1).withText("馬"));
            set.add(new Chess(1).withText("炮"));
            set.add(new Chess(1).withText("相"));
            set.add(new Chess(1).withText("仕"));
            set.add(new Chess(1).withText("帅"));
            set.add(new Chess(1).withText("兵"));
            set.add(new Chess(1).withText("王"));
            set.add(new Chess(1).withText("後"));
            set.add(new Chess(1).withText("弩")); //空占位
            set.add(new Chess(1).withText("剑")); //空占位
            set.add(new Chess()); //空占位
            //第三行
            set.add(new Chess(2).withText("车"));
            set.add(new Chess(2).withText("马"));
            set.add(new Chess(2).withText("炮"));
            set.add(new Chess(2).withText("象"));
            set.add(new Chess(2).withText("士"));
            set.add(new Chess(2).withText("将"));
            set.add(new Chess(2).withText("卒"));
            set.add(new Chess(2).withText("王"));
            set.add(new Chess(2).withText("后"));
            set.add(new Chess(2).withText("弓")); //空占位
            set.add(new Chess(2).withText("刀")); //空占位
            set.add(new Chess()); //空占位
            //第四行
            set.add(new Chess().withColor("#FF000000")); //黑色
            set.add(new Chess().withColor("#FFFFFFFF")); //白色
            set.add(new Chess().withColor("#FF808080")); //灰色
            set.add(new Chess().withColor("#FFC0C0C0")); //银色
            set.add(new Chess().withColor("#FFFF0000")); //红色
            set.add(new Chess().withColor("#FF0000FF")); //蓝色
            set.add(new Chess().withColor("#FFFFFF00")); //黄色
            set.add(new Chess().withColor("#FF00FF00")); //绿色
            set.add(new Chess().withColor("#FFFFA500")); //橙色
            set.add(new Chess().withColor("#FF00FFFF")); //青色
            set.add(new Chess().withColor("#FFFF00FF")); //紫色
            set.add(new Chess()); //空占位
            this.optionalConfigSet = new ArrayList<>(set);
        }

        @Override
        public void click(FreeQuickRoom room, int side, int row, int col) {
            //点击的是可选配置区域
            if (row > topTitleRow && row < pageTitleRow) {
                //当前分页是棋子底座
                if (pageTitles[pageIndex].equals(pageTitle_value)) {
                    Chess preset = previewChessList.get(presetIndex); //取当前编辑的预览
                    int index = (row - (topTitleRow + 1)) * maxCol + col; //从可选区域左上角数，算出索引
                    boolean isInRange = index < optionalConfigSet.size(); //判断索引是否在可选范围内
                    preset.value = isInRange ? optionalConfigSet.get(index).value : 0; //不在可选范围内就制空棋子底座
                    room.noticeAllRefreshGameInfo();
                }
                //当前分页是文字设置
                else if (pageTitles[pageIndex].equals(pageTitle_text)) {
                    //点击的是文字子分页
                    if (row == textPageTitleRow) {
                        int index = col * textPageTitles.length / maxCol;
                        //点击的是数字文字子分页
                        if (textPageTitles[index].equals(textPage_number)) {
                            switchTextPageNumber();
                            room.noticeAllRefreshGameInfo();
                        }
                        //点击的是字母文字子分页
                        else if (textPageTitles[index].equals(textPage_letter)) {
                            switchTextPageLetter();
                            room.noticeAllRefreshGameInfo();
                        }
                        //点击的是符号文字子分页
                        else if (textPageTitles[index].equals(textPage_symbol)) {
                            switchTextPageSymbol();
                            room.noticeAllRefreshGameInfo();
                        }
                        //点击的是中文文字子分页
                        else if (textPageTitles[index].equals(textPage_chinese)) {
                            switchTextPageChinese();
                            room.noticeAllRefreshGameInfo();
                        }
                        //点击的是其他文字子分页
                        else if (textPageTitles[index].equals(textPage_other)) {

                        }
                        //点击的是 自定义 文字
                        else if (textPageTitles[index].equals(textPage_custom)) {

                        }
                    }
                    //点击的是当前分页的文字
                    else {
                        Chess preset = previewChessList.get(presetIndex); //取当前编辑的预览
                        int index = (row - (topTitleRow + 1)) * maxCol + col; //从可选区域左上角数，算出索引
                        if (index < optionalConfigSet.size()) {
                            preset.withText(optionalConfigSet.get(index).text);
                        }
                        else {
                            preset.text = null;
                            preset.textColor = null;
                        }
                        room.noticeAllRefreshGameInfo();
                    }
                }
                //当前分页是颜色设置
                else if (pageTitles[pageIndex].equals(pageTitle_color)) {
                    //++区分是设置文字颜色还是，底座颜色，还是背景颜色
                    //++暂时定位设置文字颜色

                    Chess preset = previewChessList.get(presetIndex); //取当前编辑的预览
                    int index = (row - (topTitleRow + 1)) * maxCol + col; //从可选区域左上角数，算出索引
                    if (index < optionalConfigSet.size() && preset.text != null) {
                        preset.textColor = optionalConfigSet.get(index).textColor; //从预设的bgcolor中取颜色给预览的
                    }
                    room.noticeAllRefreshGameInfo();

                }
                //当前分页是常用预设
                else if (pageTitles[pageIndex].equals(pageTitle_often)) {
                    Chess preset = previewChessList.get(presetIndex); //取当前编辑的预览
                    int index = (row - (topTitleRow + 1)) * maxCol + col; //从可选区域左上角数，算出索引
                    if (index < optionalConfigSet.size()) {
                        Chess config = optionalConfigSet.get(index);
                        preset.value = config.value;
                        preset.text = config.text;
                        preset.textColor = config.textColor;
                        preset.bgColor = config.bgColor;
                        preset.borderColor = config.borderColor;
                    }
                    else {
                        preset.value = 0;
                        preset.text = null;
                        preset.textColor = null;
                        preset.bgColor = null;
                        preset.borderColor = null;
                    }
                    room.noticeAllRefreshGameInfo();
                }
            }
            //点击的是分页标题
            else if (row == pageTitleRow) {
                int index = col * pageTitles.length / maxCol;
                if (pageTitles[index].equals(pageTitle_value)) {
                    switchValuePage();
                    room.noticeAllRefreshGameInfo();
                }
                else if (pageTitles[index].equals(pageTitle_text)) {
                    switchTextPage();
                    room.noticeAllRefreshGameInfo();
                }
                else if (pageTitles[index].equals(pageTitle_color)) {
                    switchColorPage();
                    room.noticeAllRefreshGameInfo();
                }
                else if (pageTitles[index].equals(pageTitle_often)) {
                    switchOftenPage();
                    room.noticeAllRefreshGameInfo();
                }
            }
            //点击的是落子模式
            else if (row == fallModeRow) {
                previewFallModeIndex = col * fallModeTitles.length / maxCol;
                room.noticeAllRefreshGameInfo();
            }
            //点击的是预览序列
            else if (row == previewChessListRow) {
                if (col < previewChessList.size()) {
                    presetIndex = col;
                    room.noticeAllRefreshGameInfo();
                }
            }
        }

        @Override
        public void render(FreeQuickRoom room) {
            room.curGrids = GridsUtils.createEmptyGrids(maxRow, maxCol);
            room.signList.clear();
            //绘制顶部标题
            room.signList.add(new GroundSign(room.getIndex(topTitleRow, 0), room.getIndex(topTitleRow, maxCol - 1), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
            room.signList.add(new TitleSign(0.5f, 0.5f / maxRow, 0, "可选配置：", ColorCost.BLACK, 0.5f));
            //绘制分页标题
            int pageUnit = maxCol / pageTitles.length;
            for (int i = 0; i < pageTitles.length; i++) {
                int fromCol = i * pageUnit;
                int endCol = (i + 1) * pageUnit - 1;
                room.signList.add(new GroundSign(room.getIndex(pageTitleRow, fromCol), room.getIndex(pageTitleRow, endCol), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                String titleText = pageTitles[i];
                String titleColor = i == pageIndex ? ColorCost.NEW_PUT_POINT_LIGHT : ColorCost.BLACK;
                room.signList.add(new TitleSign(1f / pageTitles.length * i + 0.5f / maxCol, (pageTitleRow + 0.5f) / maxRow, 2, titleText, titleColor, 0.5f));
            }
            //绘制文本子分页标题
            if (pageTitles[pageIndex].equals(pageTitle_text)) {
                int textPageUnit = maxCol / textPageTitles.length;
                for (int i = 0; i < textPageTitles.length; i++) {
                    int fromCol = i * textPageUnit;
                    int endCol = (i + 1) * textPageUnit - 1;
                    room.signList.add(new GroundSign(room.getIndex(textPageTitleRow, fromCol), room.getIndex(textPageTitleRow, endCol), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                    String titleText = textPageTitles[i];
                    String titleColor = i == textPageIndex ? ColorCost.NEW_PUT_POINT_LIGHT : ColorCost.BLACK;
                    room.signList.add(new TitleSign(1f / textPageTitles.length * i + 0.5f / maxCol, (textPageTitleRow + 0.5f) / maxRow, 2, titleText, titleColor, 0.5f));
                }
            }
            //绘制可选配置内容集合
            for (int i = 0; i < optionalConfigSet.size(); i++) {
                Chess chess = optionalConfigSet.get(i);
                int row = i / maxCol + 1;
                int col = i % maxCol;
                chess.setPos(row, col);
                chess.drawSelf(room);
            }

            //绘制落子模式标题
            room.signList.add(new GroundSign(room.getIndex(fallModeTitleRow, 0), room.getIndex(fallModeTitleRow, maxCol - 1), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
            float previewFallModeTitleY = 1f * fallModeTitleRow / maxRow + 0.5f / maxRow;  //序列预览标题在倒数第二行,.5是为了降低半个格子
            room.signList.add(new TitleSign(0.5f, previewFallModeTitleY, 0, "落子模式：", ColorCost.BLACK, 0.5f));
            //绘制可选模式
            int fallModeUnit = maxCol / fallModeTitles.length;
            for (int i = 0; i < fallModeTitles.length; i++) {
                int fromCol = i * fallModeUnit;
                int endCol = (i + 1) * fallModeUnit - 1;
                room.signList.add(new GroundSign(room.getIndex(fallModeRow, fromCol), room.getIndex(fallModeRow, endCol), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
                String titleText = fallModeTitles[i];
                String titleColor = i == previewFallModeIndex ? ColorCost.NEW_PUT_POINT_LIGHT : ColorCost.BLACK;
                room.signList.add(new TitleSign(1f / fallModeTitles.length * i + 0.3f / maxCol, (fallModeRow + 0.5f) / maxRow, 2, titleText, titleColor, 0.4f));
            }

            //绘制预设棋子序列预览的标题
            room.signList.add(new GroundSign(room.getIndex(previewChessListTitleRow, 0), room.getIndex(previewChessListTitleRow, maxCol - 1), ColorCost.TABLECLOTH_COLOR, ColorCost.BLACK, 1f));
            float previewChessListTitleY = 1f * previewChessListTitleRow / maxRow + 0.5f / maxCol;  //序列预览标题在倒数第二行,.5是为了降低半个格子
            room.signList.add(new TitleSign(0.5f, previewChessListTitleY, 0, "序列预览：", ColorCost.BLACK, 0.5f));
            //绘制预设棋子序列的预览
            for (int i = 0; i < previewChessList.size(); i++) {
                Chess chess = previewChessList.get(i);
                chess.setPos(previewChessListRow, i); //设置棋子位置用于绘制
                chess.drawSelf(room);
                //绘制索引光标
                if (i == presetIndex) {
                    room.signList.add(new BadgeSign(room.getIndex(chess.row, chess.col), "◆", 7, ColorCost.NEW_PUT_POINT_LIGHT, ColorCost.TRANSPARENT));
                }
                else {
                    room.signList.add(new BadgeSign(room.getIndex(chess.row, chess.col), "◆", 7, ColorCost.GRAY_TRANSLUCENT, ColorCost.TRANSPARENT));
                }
            }
        }

        @Override
        public String getCoordMode() {
            return "00000000";
        }

        @Override
        public void refreshButtons(FreeQuickRoom room, Player player) {
            //落子配置-取消按钮
            CustomBottom cb1 = new CustomBottom(room, "取消", (player2) -> GameStateContainer.showGaming(room));
            //落子配置-减少按钮
            CustomBottom cb2 = new CustomBottom(room, "减少序列", (player2) -> {
                if (presetIndex > 0) {
                    previewChessList.remove(presetIndex);
                    presetIndex--;
                }
                room.noticeAllRefreshGameInfo();
            });
            //落子配置-增加按钮
            CustomBottom cb3 = new CustomBottom(room, "增加序列", (player2) -> {
                if (previewChessList.size() < PRESET_CHESSLIST_MAX) {
                    presetIndex++;
                    previewChessList.add(presetIndex, new Chess());
                }
                room.noticeAllRefreshGameInfo();
            });
            //落子配置-确认按钮
            CustomBottom cb4 = new CustomBottom(room, "采用", (player2) -> {
                //更新落子配置
                targetHand.reset(previewChessList,  fallModeTitles[previewFallModeIndex]);
                //显示对弈界面
                GameStateContainer.showGaming(room);
            });
            player.clearCustomBottom();
            player.registerCustomBottom(cb1, cb2, cb3, cb4);
        }

        /**
         * 创建一个落子预设管理器，根据当前游戏状态初始化
         */
        public static IRoomInterface createNow(FreeQuickRoom room) {
            ChessPresetManager manager = new ChessPresetManager();
            Hand hand = room.cur == 1 ? room.gameStateContainer.blackHand : room.gameStateContainer.whiteHand;
            manager.init(hand);
            return manager;
        }

    }

    /**
     * 对局信息容器
     */
    private static class GameStateContainer implements IRoomInterface {
        public String autoCacheNo; //存档编号，用于避免自动存档的重复覆盖。
        public Board board; //棋盘
        public List<Chess> boardChessList; //场上棋子
        public int step = 0;//记录回合数
        public int side = 1; //记录当前行动方
        public Hand blackHand; //黑方的手（落子处理的代理器）
        public Hand whiteHand; //白方的手（落子处理的代理器）

        @Override
        public void click(FreeQuickRoom room, int side, int row, int col) {
            if (side == 1) {
                blackHand.fall(boardChessList, row, col);
            }
            else if (side == 2) {
                whiteHand.fall(boardChessList, row, col);
            }
            room.noticeAllRefreshGameInfo();
        }

        @Override
        public void render(FreeQuickRoom room) {
            room.step = this.step;
            board.render(room);

            //++如果boardChessList长度过大，则将boardChessList分组，转成实时的缓存标记

            for (Chess chess : boardChessList) {
                chess.drawSelf(room);
            }
        }

        @Override
        public String getCoordMode() {
            return board.getCoordMode();
        }

        @Override
        public void refreshButtons(FreeQuickRoom room, Player player) {
            //落子配置按钮
            CustomBottom cb_luozipeizhi = new CustomBottom(room, "落子配置", (player2) -> {
                room.currentInteractor = ChessPresetManager.createNow(room);
                room.currentInteractor.refreshButtons(room, player);
                room.noticeAllRefreshGameInfo();
            });
            //棋盘配置按钮
            CustomBottom cb_qipanshezhi = new CustomBottom(room, "棋盘设置", (player2) -> {
                room.currentInteractor = BoardSettingManager.createNow(room);
                room.currentInteractor.refreshButtons(room, player);
                room.noticeAllRefreshGameInfo();
            });
            //存档管理按钮
            CustomBottom cb_cundangguanli = new CustomBottom(room, "存档管理", (player2) -> {
                room.noticeAllBroadcast("存档管理功能将很快实现！");
            });
            //交换控制按钮
            CustomBottom cb_jiaohuankongzhi = new CustomBottom(room, "交换控制", (player2) -> {
                //交出控制权后清空底部按钮
                player.clearCustomBottom();
                player.dispatchCustomBottom();
                //交出控制权之后增加回合数
                this.step += 1;
                //切换给对方行动
                if (room.cur == 1 && room.player_white != null) {
                    room.noticeWhiteMove();
                }
                else if (room.cur == 2 && room.player_black != null) {
                    room.noticeBlackMove();
                }
                //分配落子状态的按钮（参数当前用户）
                room.currentInteractor.refreshButtons(room, room.getCurPlayer());
            });

            player.clearCustomBottom();
            player.registerCustomBottom(cb_luozipeizhi, cb_qipanshezhi, cb_cundangguanli, cb_jiaohuankongzhi);
        }

        /**
         * 创建一个默认游戏数据
         */
        public static GameStateContainer createNormal() {
            GameStateContainer gameStateContainer = new GameStateContainer();
            gameStateContainer.board = new Board();
            gameStateContainer.boardChessList = new ArrayList<>();
            gameStateContainer.blackHand = new Hand(1);
            gameStateContainer.whiteHand = new Hand(2);
            return gameStateContainer;
        }

        /**
         * 显示局面信息
         */
        public static void showGaming(FreeQuickRoom room) {
            room.currentInteractor = room.gameStateContainer;
            room.currentInteractor.refreshButtons(room, room.getCurPlayer());
            room.noticeAllRefreshGameInfo();
        }
    }

    /**
     * 棋盘
     */
    private static class Board {
        public static final int MAX_RC = 32; //棋盘最大行数和列数   //++暂时设定为32，防止数据量过大通信异常，但是等动态缓存技术研究完成，这里可以放开很大很大
        public List<Sign> bgSignList; //背景样式
        public String bgStyle; //预设背景样式
        public int row; //棋盘行数
        public int col; //棋盘列数
        public String coordMode; //坐标模式

        public Board() {
            this.row = 15;
            this.col = 15;
            this.coordMode = "33000000";
            this.bgStyle = BoardSettingManager.style_square;
        }

        public Board(int row, int col) {
            this.row = row;
            this.col = col;
            this.coordMode = "33000000";
            this.bgStyle = BoardSettingManager.style_square;
        }

        public void render(FreeQuickRoom room) {
            room.curGrids = GridsUtils.createEmptyGrids(row, col);
            room.signList.clear();
            if (bgStyle.equals(BoardSettingManager.style_square)){
                bgSignList = null;
            }
            else if (bgStyle.equals(BoardSettingManager.style_crossLine)){
                bgSignList = new ArrayList<>();
                int maxRow = room.curGrids.length - 1;
                int maxCol = room.curGrids[0].length - 1;
                //背景
                bgSignList.add(new GroundSign(room.getIndex(0, 0), room.getIndex(maxRow, maxCol), ColorCost.TABLECLOTH_COLOR, ColorCost.TABLECLOTH_COLOR, 2f, 0, 0));
                //外边框
                bgSignList.add(getPath(room,0, 0, 0, maxCol, 1));
                bgSignList.add(getPath(room,maxRow, 0, maxRow, maxCol, 1));
                bgSignList.add(getPath(room,0, 0, maxRow, 0, 1));
                bgSignList.add(getPath(room,0, maxCol, maxRow, maxCol, 1));
                //线条
                for (int i = 1; i < maxRow; i++) {
                    bgSignList.add(getPath(room,i, 0, i, maxCol, 1));
                }
                for (int i = 1; i < maxCol; i++) {
                    bgSignList.add(getPath(room,0, i, maxRow, i, 1));
                }
            }
            else if (bgStyle.equals(BoardSettingManager.style_empty)){
                bgSignList = new ArrayList<>();
                bgSignList.add(new GroundSign(room.getIndex(0, 0), room.getIndex(row - 1, col - 1), ColorCost.TABLECLOTH_COLOR, ColorCost.TABLECLOTH_COLOR, 5f));
            }
            else {
                bgSignList = null;
            }
            if (bgSignList != null) {
                room.signList.addAll(bgSignList);
            }
        }

        private PathSign getPath(FreeQuickRoom room,final int fRow, final int fCol, final int tRow, final int tCol, final float size) {
            List<Pair<Integer, Integer>> nodes = new ArrayList<>();
            nodes.add(new Pair<>(room.getIndex(fRow, fCol), 4));
            nodes.add(new Pair<>(room.getIndex(tRow, tCol), 4));
            return new PathSign(nodes, ColorCost.BLACK, ColorCost.BLACK, size, 0);
        }

        public String getCoordMode() {
            return coordMode;
        }

        public static Board clone(Board board) {
            Board newBoard = new Board(board.row, board.col);
            newBoard.coordMode = board.coordMode;
            newBoard.bgStyle = board.bgStyle;
            if (board.bgSignList != null) {
                newBoard.bgSignList = new ArrayList<>(board.bgSignList);
            }
            return newBoard;
        }
    }

    /**
     * 棋子
     */
    private static class Chess {
        public int value; //对应棋子索引
        public String text; //棋子上的文字（当前最多支持两个）
        public String textColor; //棋子上文字的颜色(有文字的话必定有文字颜色)
        public String bgColor; //棋子背景颜色
        public String borderColor; //棋子边框颜色，有边框
        public int row; //棋子所在行
        public int col; //棋子所在列

        public Chess() {
        }

        public Chess(int value) {
            this.value = value;
        }

        //设置棋子位置
        public void setPos(int row, int col) {
            this.row = row;
            this.col = col;
        }

        //判断是否是同一位置
        public boolean isSamePos(int row, int col) {
            return this.row == row && this.col == col;
        }

        //判断该棋子是否没有任何有效内容，如果是的话，该Chess不会被添加到GameState上的boardChessList中
        public boolean isInvalid() {
            return value != 0 || text != null || bgColor != null || borderColor != null;
        }

        //棋子自身绘制
        public void drawSelf(FreeQuickRoom room) {
            //必有底座，空用0
            room.curGrids[row][col] = value;
            //文字的绘制
            if (text != null) {
                //单个文字的绘制
                if (text.length() == 1) {
                    room.signList.add(new TextSign(room.getIndex(row, col), text, textColor));
                }
                //双个文字的绘制
                else if (text.length() == 2) {
                    room.signList.add(new TitleSign((col + 0.5f) / room.curGrids[0].length, (row + 0.5f) / room.curGrids.length, 0, text, textColor, 0.3f));
                }
            }
            //根据不同配比用不同的方式绘制边框和底色
            if (bgColor != null && borderColor != null) {
                room.signList.add(new GroundSign(room.getIndex(row, col), bgColor, borderColor, 1f));
            }
            else if (borderColor != null) {
                room.signList.add(new GroundSign(room.getIndex(row, col), ColorCost.TRANSPARENT, borderColor, 1f));
            }
            else if (bgColor != null) {
                room.signList.add(new ColorSign(room.getIndex(row, col), bgColor));
            }
        }

        //设置文字。并返回自身
        public Chess withText(String text) {
            //添加文字，最多两个字符，已经有两个字符的就变回1个
            if (this.text == null || this.text.length() == 2) {
                this.text = text;
            }
            else {
                this.text += text;
            }
            //只要添加了文字，就要设置默认的文字颜色
            if (textColor == null){
                if (value == 1 || value == 11) {
                    this.textColor = ChessPresetManager.DEFAULT_TEXT_COLOR_WHITE;
                }
                else if (value == 8) {
                    this.textColor = ChessPresetManager.DEFAULT_TEXT_COLOR_GRAY;
                }
                else {
                    this.textColor = ChessPresetManager.DEFAULT_TEXT_COLOR_BLACK;
                }
            }
            return this;
        }

        //设置文字和文字的颜色。并返回自身
        public Chess withTextAndColor(String text,  String textColor) {
            //添加文字，最多两个字符，已经有两个字符的就变回1个
            this.text = text;
            this.textColor = textColor;
            return this;
        }

        //设置颜色。并返回自身
        public Chess withColor(String color) {
            this.bgColor = color;
            return this;
        }

        /**
         * 克隆棋子
         */
        public static Chess clone(Chess chess) {
            Chess newChess = new Chess();
            newChess.value = chess.value;
            newChess.text = chess.text;
            newChess.textColor = chess.textColor;
            newChess.bgColor = chess.bgColor;
            newChess.borderColor = chess.borderColor;
            newChess.row = chess.row;
            newChess.col = chess.col;
            return newChess;
        }

    }

    /**
     * 手（落子行为处理的代理）
     */
    private static class Hand {

        public static final int STACK_MAX = 100; //抓取堆栈容量上限（设上限是为了防止仅存在理论上的爆堆行为）

        public List<Chess> chessStack; //抓取堆栈
        public List<Chess> chessSequence; //手持预设棋子序列
        public String fallMode;
        private transient int[] lastPoint; //记录上一步点击的位置[row,col]
        private transient int repeatCount; //记录同一位置连续点击的次数

        public Hand(int side) {
            if (side == 1) {
                chessStack = new ArrayList<>();
                chessSequence = new ArrayList<>();
                chessSequence.add(new Chess(1));
                fallMode = ChessPresetManager.fallMode_switch;
            }
            else {
                chessStack = new ArrayList<>();
                chessSequence = new ArrayList<>();
                chessSequence.add(new Chess(2));
                fallMode = ChessPresetManager.fallMode_switch;
            }
        }

        //更新预设
        public void reset(List<Chess> previewChessList, String previewFallMode) {
            lastPoint = null;
            repeatCount = 0;
            chessSequence.clear();
            for (Chess chess : previewChessList) {
                chessSequence.add(Chess.clone(chess));
            }
            fallMode = previewFallMode;
        }

        //从boardChessList中找 row col位置的棋子
        private static Chess findChessByPos(List<Chess> boardChessList, int row, int col) {
            return boardChessList.stream().filter(chess1 -> chess1.isSamePos(row, col)).findFirst().orElse(null);
        }

        //添加棋子对象到场上
        private static void addChessOnBoard(List<Chess> boardChessList, int row, int col, Chess chess) {
            if (chess.isInvalid()) {
                chess.setPos(row, col);
                boardChessList.add(chess);
            }
        }

        //场上删除指定位置的棋子
        private static void removeChessFromBoard(List<Chess> boardChessList, int row, int col) {
            boardChessList.removeIf(chess -> chess.isSamePos(row, col));
        }

        //从预设序列中获取棋子对象克隆副本
        private Chess getFromPresetSequence(int index) {
            return Chess.clone(chessSequence.get(index));
        }

        // 落子逻辑处理分派
        public void fall(List<Chess> boardChessList, int row, int col) {

            //连点切换模式
            if (fallMode.equals(ChessPresetManager.fallMode_switch)) {
                fallSwitch(boardChessList, row, col);
            }
            //选子移动模式
            else if (fallMode.equals(ChessPresetManager.fallMode_choose)) {
                fallChoose(boardChessList, row, col);
            }
            //选取堆叠模式
            else if (fallMode.equals(ChessPresetManager.fallMode_stack)) {
                fallStack(boardChessList, row, col);
            }
            //随机落子模式
            else if (fallMode.equals(ChessPresetManager.fallMode_random)) {
                fallRandom(boardChessList, row, col);
            }
            //线性铺设模式
            else if (fallMode.equals(ChessPresetManager.fallMode_line)) {
                fallLine(boardChessList, row, col);
            }
            //矩形铺设模式
            else if (fallMode.equals(ChessPresetManager.fallMode_rectangle)) {
                fallRectangle(boardChessList, row, col);
            }
            //位置的就默认改为连点切换模式
            else {
                fallSwitch(boardChessList, row, col);
            }

            //临时测试用
            //testFall(boardChessList, row, col);
        }

        //连点切换模式落子：点击空格落下序列的第一个棋子，再次点击相同位置落下序列的下一个棋子，点击其它棋子抢占位置
        private void fallSwitch(List<Chess> boardChessList, int row, int col) {
            int[] newPoint = new int[]{row, col};
            //落在老位置上时
            if (Arrays.equals(lastPoint, newPoint)) {
                //更新计数，以实现循环切换棋子的效果
                repeatCount++;
                if (repeatCount == chessSequence.size()) {
                    repeatCount = 0;
                }
            }
            //落在新位置上时
            else {
                //重置计数
                repeatCount = 0;
            }

            //先删掉这个位置原来的棋子
            removeChessFromBoard(boardChessList, row, col);
            //根据序列取手上的预设棋子，复制到场上
            Chess chess = getFromPresetSequence(repeatCount);
            addChessOnBoard(boardChessList, row, col, chess);
            //落子成功才更新坐标
            lastPoint = newPoint;
        }

        //选子移动模式落子
        private void fallChoose(List<Chess> boardChessList, int row, int col) {
            //从boardChessList中找 row col位置的棋子
            Chess chess = findChessByPos(boardChessList, row, col);
            //栈为空，选取棋子
            if (chessStack.size() == 0) {
                if (chess != null) {
                    boardChessList.remove(chess);
                    chessStack.add(chess);
                }
            }
            //栈有棋子，落下
            else {
                boardChessList.remove(chess);
                addChessOnBoard(boardChessList, row, col, chessStack.get(0));
                chessStack.remove(0);
            }
        }

        //选取堆叠模式落子
        private void fallStack(List<Chess> boardChessList, int row, int col) {
            Chess chess = findChessByPos(boardChessList, row, col);
            //落在有棋子的地方就把棋子捡起
            if (chess != null) {
                if (chessStack.size() < STACK_MAX) {
                    boardChessList.remove(chess);
                    chessStack.add(0, chess);
                }
            }
            //落在空格就从栈中取一个棋子落下
            else {
                if (chessStack.size() > 0) {
                    addChessOnBoard(boardChessList, row, col, chessStack.get(0));
                    chessStack.remove(0);
                }
            }
        }

        //随机落子模式落子，仅仅在空位上才会落子，不会强制占用原有棋子
        private void fallRandom(List<Chess> boardChessList, int row, int col) {
            //先删掉这个位置原来的棋子
            removeChessFromBoard(boardChessList, row, col);
            //随机取一个预设棋子
            int randomIndex = (int) (Math.random() * chessSequence.size());
            //添加到场上
            addChessOnBoard(boardChessList, row, col, getFromPresetSequence(randomIndex));
        }

        //线性铺设模式落子
        private void fallLine(List<Chess> boardChessList, int row, int col) {
            if (lastPoint == null) {
                removeChessFromBoard(boardChessList, row, col);
                addChessOnBoard(boardChessList, row, col, getFromPresetSequence(0));
                lastPoint = new int[]{row, col};
            }
            else {
                //从lastPoint到newPoint之间一条线落子
                List<int[]> linePoints = GridsUtils.getLineGridsBetweenPoints(lastPoint[0], lastPoint[1], row, col);
                for (int i = 0; i < linePoints.size(); i++) {
                    int[] point = linePoints.get(i);
                    removeChessFromBoard(boardChessList, point[0], point[1]);
                    int index = i % chessSequence.size(); //按顺序从预设棋子序列中取
                    addChessOnBoard(boardChessList, point[0], point[1], getFromPresetSequence(index));
                }
                lastPoint = null;
            }
        }

        //矩形铺设模式落子
        private void fallRectangle(List<Chess> boardChessList, int row, int col) {
            if (lastPoint == null) {
                removeChessFromBoard(boardChessList, row, col);
                addChessOnBoard(boardChessList, row, col, getFromPresetSequence(0));
                lastPoint = new int[]{row, col};
            }
            else {
                //获取从lastPoint到newPoint之间形成的矩形的所有格子坐标，先左右顺序，再上下顺序
                List<int[]> rectPoints = GridsUtils.getRectGridsBetweenPointsRight(lastPoint[0], lastPoint[1], row, col);
                for (int i = 0; i < rectPoints.size(); i++) {
                    int[] point = rectPoints.get(i);
                    removeChessFromBoard(boardChessList, point[0], point[1]);
                    int index = i % chessSequence.size(); //按顺序从预设棋子序列中取
                    addChessOnBoard(boardChessList, point[0], point[1], getFromPresetSequence(index));
                }
                lastPoint = null;
            }
        }

    }

}